/*
 *  \file Voc.h
 *  gre
 *
 *  Created by Kutlak Roman on 10/03/2012.
 *  Copyright 2012 Kutlak Roman. All rights reserved.
 *
 */

#ifndef Voc_h 
#define Voc_h

#include <string>
#include <memory>
#include <map>
#include <mutex>


#include "Utilities.h"

namespace kutlak {
namespace roman {
namespace gre {
    
    
    // max number of indexes is std::numeric_limits<Vindex_t>::max()
    typedef unsigned long long Vindex_t;
    
    /*!
     * \brief A class for converting strings to numbers.
     *
     * The class maps strings to indexes using Index() 
     * method and indexes to strings using the String()
     * method.
     *
     * The Voc::Index(...) can throw IndexException 
     * if the vocabulary runs out of indexes for strings.
     * This should be handled in KnowledgeBase and Ontology
     * classes when loading files.
     */
    class Voc { 
    public: 
        // std::map is used to map string to index
        typedef std::map<std::string, Vindex_t> VocMap_t;
        
        // This exception is thrown when there are more strings than available
        // indexes.
        struct IndexException : public std::runtime_error 
        {
            IndexException(const std::string& exc) : 
            std::runtime_error(exc) {}
            
            IndexException(std::string&& exc) : 
            std::runtime_error(std::move(exc)) {}
            
        };
        
#pragma mark -
#pragma mark lifecycle
        
        // Session has to be able to construct an empty Voc.
        friend class Session;
        
        // Voc is implemented as a singleton class.
    private:
        Voc();
        
        Voc(const Voc& from) = delete;
        
        Voc(Voc&& from) = delete;
        
        Voc& operator=(Voc from) = delete;
        
    public:
        ~Voc(void) noexcept(true);
        
        // swap is useful for exception-safe deserialisation
        friend void swap(Voc& rhs, Voc& lhs) noexcept(true);
        
        // returns the reference to the only instance
        static Voc& Instance();
        
#pragma mark -
#pragma mark I/O
        
        // The Voc class is serialisable
        friend std::ostream& operator<<(std::ostream& os, const Voc& v);
        
        friend std::istream& operator>>(std::istream& is, Voc& v);
        
        void Serialise(std::ostream& os) const;
        
        void Deserialise(std::istream& is);
        
#pragma mark -
#pragma mark operations
        
        /*! \brief Method maps a string to an index.
         *  If the string already exists, the index is returnded.
         *  If not, a new index is created.
         *
         * \exception IndexException when too many strings are mapped
         */
        Vindex_t Index(const std::string& str);
        Vindex_t Index(std::string&& str);
        
        //! Returns a string corresponding to the index or an empty string.
        std::string String(const Vindex_t& idx) const;
        
#pragma mark -
#pragma mark access
        //! Returns the Voc map.
        const VocMap_t& Map() const {
            return mMap;
        }
        
        // this should probably be only used for serialisation
        void Clear() {
            nWords = 0;
            mMap.clear();
        }
        
#pragma mark -
#pragma mark inquiry
        
        //! Number of strings mapped to indices.
        Vindex_t NumWords() const {
            return nWords;
        }
        
        
#pragma mark -
#pragma mark constants
        
    private:
        Vindex_t  nWords; //!< Number of strings in the vocabulary
        VocMap_t  mMap;   //!< Mappings of strings and indices.
        
        static std::once_flag sFlag; // flag for initialization
    };
    
} // end namespace gre
}
}
#endif // Voc_h
